﻿using Manon.Repository.Collections;
using Manon.Repository.Repository;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Manon.Repository.Extensions
{
    public static class FilterExtension
    {
        public static Expression<Func<T, bool>> ToGovConditionExpression<T>(IReposUser reposUser)
        {

            List<Filter> keyValues = new List<Filter>();

            var interFaces = typeof(T).GetInterfaces();
            if (interFaces == null || interFaces.Any() == false)
            {
                return keyValues.ToConditionExpression<T>();
            }

            if (interFaces.Any(n => n.Name == "IDeleteEntity"))
            {
                keyValues.Add(new Filter()
                {
                    FilterType = FilterTypeEnum.Equal,
                    Key = "IsDeleted",
                    Value = "false"
                });
            }


            //if (reposUser != null && interFaces != null && interFaces.Any(n => n.Name == "IGovernmentEntity"))
            //{
            //    if (reposUser.GovFirstId > 0)
            //    {
            //        keyValues.Add(new Filter()
            //        {
            //            FilterType = FilterTypeEnum.Equal,
            //            Key = "GovFirstId",
            //            Value = reposUser.GovFirstId.ToString()
            //        });
            //    }
            //    if (reposUser.GovSecId > 0)
            //    {
            //        keyValues.Add(new Filter()
            //        {
            //            FilterType = FilterTypeEnum.Equal,
            //            Key = "GovSecId",
            //            Value = reposUser.GovSecId.ToString()
            //        });
            //    }
            //    if (reposUser.GovThirdId > 0)
            //    {
            //        keyValues.Add(new Filter()
            //        {
            //            FilterType = FilterTypeEnum.Equal,
            //            Key = "GovThirdId",
            //            Value = reposUser.GovThirdId.ToString()
            //        });
            //    }
            //}
            //if (reposUser != null && interFaces != null && interFaces.Any(n => n.Name == "ITenantEntity"))
            //{
            //    //目前只过滤工地端用户类型 工地管理员/工地普通用户会按照租户过滤
            //    if (reposUser.UserType == 16 || reposUser.UserType == 32)
            //    {
            //        keyValues.Add(new Filter()
            //        {
            //            FilterType = FilterTypeEnum.Equal,
            //            Key = "TenantId",
            //            Value = reposUser.TenantId.ToString()
            //        });
            //    }
            //}

            return keyValues.ToConditionExpression<T>();
        }


        public static Expression<Func<T, bool>> ToConditionExpression<T>(this IList<Filter> keyValues)
        {
            Expression predicateBody = Expression.Constant(true, typeof(bool));
            var parameterExp = Expression.Parameter(typeof(T));

            if (keyValues != null)
            {
                foreach (var keyValue in keyValues)
                {
                    var left = Expression.Property(parameterExp, keyValue.Key);
                    //determine the property type from domain class
                    var propertyType = ((PropertyInfo)left.Member).PropertyType;
                    var converter = TypeDescriptor.GetConverter(propertyType);
                    if (!converter.CanConvertFrom(typeof(string)))
                        throw new NotSupportedException();
                    Expression right = Expression.Constant(converter.ConvertFromInvariantString(keyValue.Value));
                    Expression expression;
                    switch (keyValue.FilterType)
                    {
                        case FilterTypeEnum.LessThanOrEqual:
                            if (propertyType.TypeCanBeCompared())
                            {
                                expression = LessThanOrEqual(left, right);
                            }
                            else
                            {
                                throw new NotSupportedException();
                            }
                            break;
                        case FilterTypeEnum.GreaterThanOrEqual:
                            if (propertyType.TypeCanBeCompared())
                            {
                                expression = GreaterThanOrEqual(left, right);
                            }
                            else
                            {
                                throw new NotSupportedException();
                            }
                            break;
                        case FilterTypeEnum.LessThan:

                            if (propertyType.TypeCanBeCompared())
                            {
                                expression = LessThan(left, right);
                            }
                            else
                            {
                                throw new NotSupportedException();
                            }
                            break;
                        case FilterTypeEnum.GreaterThan:

                            if (propertyType.TypeCanBeCompared())
                            {
                                expression = GreaterThan(left, right);
                            }
                            else
                            {
                                throw new NotSupportedException();
                            }
                            break;
                        case FilterTypeEnum.Equal:
                            expression = Expression.Equal(left, right);
                            break;
                        case FilterTypeEnum.Contains:
                            expression = Contains(left, right);
                            break;
                        default:
                            throw new ArgumentException("Filter Type could not be determined");
                    }
                    if (keyValue.FilterLogic == FilterLogicEnum.AndAlso)
                    {
                        predicateBody = Expression.AndAlso(predicateBody, expression);
                    }
                    else
                    {
                        predicateBody = Expression.Or(predicateBody, expression);
                    }

                }
            }
            return Expression.Lambda<Func<T, bool>>(predicateBody, parameterExp);
        }

        static bool TypeCanBeCompared(this Type type)
        {
            return type.GetInterfaces().Contains(typeof(IComparable));
        }

        static Expression GreaterThanOrEqual(Expression left, Expression right)
        {
            if (IsNullableType(left.Type) && !IsNullableType(right.Type))
                right = Expression.Convert(right, left.Type);
            else if (!IsNullableType(left.Type) && IsNullableType(right.Type))
                left = Expression.Convert(left, right.Type);
            return Expression.GreaterThanOrEqual(left, right);
        }

        static Expression GreaterThan(Expression left, Expression right)
        {
            if (IsNullableType(left.Type) && !IsNullableType(right.Type))
                right = Expression.Convert(right, left.Type);
            else if (!IsNullableType(left.Type) && IsNullableType(right.Type))
                left = Expression.Convert(left, right.Type);
            return Expression.GreaterThan(left, right);
        }

        static Expression LessThan(Expression left, Expression right)
        {
            if (IsNullableType(left.Type) && !IsNullableType(right.Type))
                right = Expression.Convert(right, left.Type);
            else if (!IsNullableType(left.Type) && IsNullableType(right.Type))
                left = Expression.Convert(left, right.Type);
            return Expression.LessThan(left, right);
        }

        static Expression LessThanOrEqual(Expression left, Expression right)
        {
            if (IsNullableType(left.Type) && !IsNullableType(right.Type))
                right = Expression.Convert(right, left.Type);
            else if (!IsNullableType(left.Type) && IsNullableType(right.Type))
                left = Expression.Convert(left, right.Type);
            return Expression.LessThanOrEqual(left, right);
        }

        static Expression Contains(Expression left, Expression right)
        {
            //var toStringMethod = typeof(object).GetMethod("ToString");
            //left = Expression.Call(left, toStringMethod);
            //right = Expression.Call(right, toStringMethod);
            //var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            //return Expression.Call(left, containsMethod, right);
            var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            return Expression.Call(left, containsMethod, right);
        }

        static bool IsNullableType(Type type)
        {
            return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }
    }
}